using System;
using System.Linq;
using LS.UnityExtensions;

namespace LS.Digitize
{
    public class ReflectedSerializer: CompoundSerializer, IReflectedSerializer
    {
        public override bool IsReflectedType => true;

        public ReflectedSerializer(Type type) : base(type, typeof(Type))
        {
            Members = SerializerUtil.GetMembers(type);
        }

        public virtual void WriteMember(IWriter writer, object obj, Member member)
        {
            ISerializer serializer = SerializerUtil.GetOrCreateSerializer(member.type);
            using (writer.WritePropertyScope(member.name))
            {
                serializer.Write(writer,member.reflectedMember.GetValue(obj));
            }
        }

        public virtual void ReadMember(IReader reader, object obj, Member member)
        {
            ISerializer serializer = SerializerUtil.GetOrCreateSerializer(member.type);
            member.reflectedMember.SetValue(obj, serializer.Read(reader));
        }

        public override void Write(IWriter writer, object obj)
        {
            if (obj == null)
            {
                writer.WriteNull();
                return;
            }
            
            ISerializer<Type> typeSerializer = SubSerializer as ISerializer<Type>;
            using (writer.WriteObjectScope())
            {
                bool isBreak = false;
                var objType = obj.GetType();
                if (objType != DataType)
                {
                    using (writer.WritePropertyScope(TypeFieldName))
                        typeSerializer.Write(writer, obj);

                    using (writer.WritePropertyScope(ValueFieldName))
                        SerializerUtil.GetOrCreateSerializer(objType).Write(writer, obj);
                    isBreak = true;
                }

                if (!isBreak)
                {
                    for (int i = 0; i < Members.Length; i++)
                    {
                        var member = Members[i];
                        WriteMember(writer, obj, member);
                    }
                }
            }
        }

        public override object Read(IReader reader)
        {
            string propertyName = null;
            ISerializer<Type> typeSerializer = GetSubSerializer(SubTypes.Count - 1) as ISerializer<Type>;
            Type type = null;
            object obj = null;
            bool isDerivedType = false;
            using (var objScope = reader.ReadObjectScope())
            {
                if (!objScope.ReadFailure)
                {
                    if (reader.CouldBeNull())
                    {
                        reader.ReadNull();
                        return null;
                    }
                    
                    while (true)
                    {
                        propertyName = reader.ReadPropertyName();
                        if(string.IsNullOrEmpty(propertyName))
                            break;
                        
                        if (propertyName == TypeFieldName)
                        {
                            type = typeSerializer.ReadData(reader);
                            if(type != DataType)
                                isDerivedType = true;
                        }
                        else if(propertyName == ValueFieldName && isDerivedType)
                        {
                            ISerializer serializer = SerializerUtil.GetOrCreateSerializer(type);
                            obj = serializer.Read(reader);
                            break;
                        }
                        else
                        {
                            if (type == null)
                            {
                                type = DataType;
                                obj = LSReflection.CreateInstance(type);
                            }
                            
                            var member = Members.FirstOrDefault(m => m.name == propertyName);
                            if (member != null)
                            {
                                ReadMember(reader, obj, member);
                            }
                        }
                    }
                }
            }
            return obj;
        }

        public override void ReadInto(IReader reader, ref object obj)
        {
            throw new NotImplementedException();
        }
    }
}